﻿namespace IBTrader.Frontend.TestClient
{
    using AxTWSLib;
    using IBTrader.Modules;
    using IBTrader.Modules.Configuration;
    using IBTrader.Modules.Export;
    using IBTrader.Modules.HistoricalData;
    using IBTrader.Modules.Tws;
    using IBTrader.Properties;
    using Microsoft.Practices.Prism.Commands;
    using NLog;
    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Reflection;
    using System.Windows;
    using System.Windows.Threading;

    class ViewModel : ViewModelBase
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();
        private int lastId;
        private HistoricalData historicalData;
        private readonly DispatcherTimer timer = new DispatcherTimer();
        private event EventHandler PacingViolation;
        #region delegate commands
        public DelegateCommand RequestAccountUpdates { get; private set; }
        public DelegateCommand RequestNewsBulletins { get; private set; }
        public DelegateCommand CancelOrder { get; private set; }
        public DelegateCommand RequestOpenOrders { get; private set; }
        public DelegateCommand ExportExecutions { get; private set; }
        public DelegateCommand HistoricalMinutesRealtime { get; private set; }
        public DelegateCommand HistoricalSecondsRealtime { get; private set; }
        public DelegateCommand HistoricalMinutesExtended { get; private set; }
        public DelegateCommand HistoricalSecondsExtended { get; private set; }
        public DelegateCommand RequestContractDetails { get; private set; }
        public DelegateCommand RequestMarketDepth { get; private set; }
        public DelegateCommand RequestRealTimeBars { get; private set; }
        public DelegateCommand RequestMarketData { get; private set; }
        public DelegateCommand RequestFundamentalData { get; private set; }
        public DelegateCommand CancelMarketData { get; private set; }
        public DelegateCommand CancelMarketDepth { get; private set; }
        public DelegateCommand CancelRealTimeBar { get; private set; }
        public DelegateCommand CancelHistorical { get; private set; }
        public DelegateCommand CancelFundamentalData { get; private set; }
        public DelegateCommand PlaceOrder { get; private set; }
        public DelegateCommand UpdateOrder { get; private set; }
        #endregion
        #region properties
        public string Symbol { get; set; }
        public string SecurityType { get; set; }
        public string Currency { get; set; }
        public string Multiplier { get; set; }
        public string Exchange { get; set; }
        public string PrimaryExchange { get; set; }
        public string Price { get; set; }
        public string Action { get; set; }
        public string OrderType { get; set; }
        public string BarType { get; set; }
        public string FundamentalData { get; set; }
        public DateTime DownloadFrom { get; set; }
        public DateTime DownloadTo { get; set; }
        #endregion
        public ViewModel() : base(ComponentsSection.Components.TestClient)
        {
            if ((bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(DependencyObject)).Metadata.DefaultValue) return;
            #region Timer
            timer.Interval = new TimeSpan(0, 1, 0);
            PacingViolation += (__, _) =>
            {
                LogInfo("Timer", "Wait {0}", timer.Interval);
                timer.Start(); 
            };
            timer.Tick += (x, y) =>
            {
                timer.Stop();
                if (historicalData != null && !historicalData.Error.Paused)
                    historicalData.Data.Request();
            };
            #endregion
            #region Initialize
            Currency = "USD";
            Exchange = "SMART";
            DownloadFrom = DateTime.Today.AddYears(-1);
            DownloadTo = DateTime.Today;
            #endregion            
            #region Order
            CancelOrder = new DelegateCommand(() => IB.cancelOrder(lastId));
            RequestOpenOrders = new DelegateCommand(() => IB.reqOpenOrders());
            #endregion
            #region News
            RequestAccountUpdates = new DelegateCommand(() => IB.reqAccountUpdates(1, "1"));
            RequestNewsBulletins = new DelegateCommand(() => IB.reqNewsBulletins(true));
            CancelMarketData = new DelegateCommand(() => IB.cancelMktData(lastId));
            CancelMarketDepth = new DelegateCommand(() => IB.cancelMktDepth(lastId));
            CancelRealTimeBar = new DelegateCommand(() => IB.cancelRealTimeBars(lastId));
            CancelFundamentalData = new DelegateCommand(() => IB.cancelFundamentalData(lastId)); 
            #endregion
            #region Info
            RequestContractDetails = new DelegateCommand(() =>  IB.reqContractDetails(++lastId, CreateContract()));
            RequestMarketDepth = new DelegateCommand(() =>  IB.reqMktDepth(++lastId, CreateContract(), 10));
            RequestRealTimeBars = new DelegateCommand(() =>  IB.reqRealTimeBars(++lastId, CreateContract(), 5, BarType, false));
            RequestMarketData = new DelegateCommand(() => IB.reqMktData(++lastId, CreateContract(), "", false));
            RequestFundamentalData = new DelegateCommand(() => IB.reqFundamentalData(++lastId, CreateContract(), FundamentalData));
            #endregion
            #region Order
            PlaceOrder = new DelegateCommand(() => ExecuteOrder(++lastId));
            UpdateOrder = new DelegateCommand(() => ExecuteOrder(lastId));
            #endregion            
            #region Export
            ExportExecutions = new DelegateCommand(() => {
                var export = new ExportExecutions(IB);
                var fileName = export.DefaultFileName(IB.ClientId);
                export.Start(fileName, DownloadFrom);
                LogInfo("ExportExecutions", "Exported to File {0} [time:{0}] ", fileName, DownloadTo);
            });
            Func<bool> canExecute = () => historicalData == null;
            HistoricalMinutesRealtime = new DelegateCommand(() => DownloadHistoricalData(new TimeSpan(1, 0, 0, 0), "1 min", true), () => historicalData == null);
            HistoricalSecondsRealtime = new DelegateCommand(() => DownloadHistoricalData(new TimeSpan(0, 30, 0), "1 secs", true), () => historicalData == null);
            HistoricalMinutesExtended = new DelegateCommand(() => DownloadHistoricalData(new TimeSpan(1, 0, 0, 0), "1 min", false), () => historicalData == null);
            HistoricalSecondsExtended = new DelegateCommand(() => DownloadHistoricalData(new TimeSpan(0, 30, 0), "1 secs", false), () => historicalData == null);
            CancelHistorical = new DelegateCommand(() => historicalData.Cancel(), () => historicalData != null);
            #endregion
        }
        protected override void OnLoad(System.Windows.Controls.Control view)
        {
            if (IsLoaded) return;
            if (IB == null) return;

            IB.nextValidId += (_, e) => lastId = e.id;
            IB.errMsg += (_, e) => { if (historicalData != null) historicalData.Error.Handle(e); };
            IB.tickPrice += (o, i) => LogInfo("tickPrice", "{0} {1} {2}", i.price, i.tickType, i.id);
            IB.tickSize += (o, i) => LogInfo("tickSize", "{0} {1} {2}", i.size, i.tickType, i.id);
            IB.tickGeneric += (o, i) => LogInfo("tickGeneric", "{0} {1} {2}", i.value, i.tickType, i.id);
            IB.historicalData += (_, i) =>
            {
                if (historicalData != null)
                    historicalData.File.Write(i);
                else
                    LogInfo("historicalData", "{0} {1} {2} {3} {4}", i.date, i.open, i.high, i.low, i.close);
            };
            IB.historicalDataEnd += (_, i) =>
            {
                if (historicalData != null)
                    historicalData.Next();
                else
                    LogInfo("historicalDataEnd");
            };                

            IB.contractDetails += (o, i) =>
            {
                LogObject(typeof(IBApi.ContractDetails), i.ContractDetails);
                LogObject(typeof(IBApi.Contract), (i.ContractDetails as IBApi.ContractDetails).Summary as IBApi.Contract);
            };
            IB.contractDetailsEnd += (o, i) => LogHeader("contractDetailsEnd", i.reqId.ToString());

            IB.updatePortfolio += (o, i) =>
            {
                LogObject(typeof(UpdatePortfolioEventArgs), i);
                LogObject(typeof(IBApi.Contract), i.contract);
            };
            IB.updateMktDepth += (o, i) => LogObject(typeof(_DTwsEvents_updateMktDepthEvent), i);
            IB.updateMktDepthL2 += (o, i) => LogObject(typeof(_DTwsEvents_updateMktDepthL2Event), i);
            IB.realtimeBar += (o, i) => LogObject(typeof(_DTwsEvents_realtimeBarEvent), i);
            IB.updateAccountValue += (o, i) => LogObject(typeof(_DTwsEvents_updateAccountValueEvent), i);
            IB.updateAccountTime += (o, i) => LogObject(typeof(_DTwsEvents_updateAccountTimeEvent), i);

            IB.openOrder += (o, i) =>
            {
                LogHeader("openOrder", "Start");
                LogInfo("orderId", i.orderId);
                LogObject(typeof(IBApi.Contract), i.contract);
                LogObject(typeof(IBApi.Order), i.order);
                LogObject(typeof(IBApi.OrderState), i.orderState);
                LogHeader("openOrder", "End");
            };

            IB.updateNewsBulletin += (o, e) => LogInfo("NewsBulletin", "{0} {1} {2} {3}", e.msgId, e.msgType, e.origExchange, e.message);
            IB.fundamentalData += (o, e) => LogInfo("NewsBulletin", "{0} {1}", e.reqId, e.data);

            base.OnLoad(view);
        }
        private void DownloadHistoricalData(TimeSpan barDuration, string barSize, bool onlyRealTime)
        {
            var commands = new[] { HistoricalMinutesRealtime, HistoricalMinutesExtended, HistoricalSecondsExtended, HistoricalSecondsRealtime, CancelHistorical };
            var contract = new Model.HistoricalContract { Symbol = Symbol, SecurityType = SecurityType, Exchange = Exchange, Currency = Currency, 
                Multiplier = Multiplier, PrimaryExchange = PrimaryExchange, BarDuration = barDuration, BarSize = barSize, BarType = "TRADES",
                From =DownloadFrom, To = DownloadTo, OnlyRealTime = onlyRealTime};
            historicalData = new HistoricalData(IB, contract);
            historicalData.Data.Next += () => LogInfo("HistoricalData", "{0} {1}", historicalData.Contract.Symbol, historicalData.Contract.DateTime);
            historicalData.Error.PacingViolation += PacingViolation;
            historicalData.Contract.End += () =>
            {
                LogInfo("HistoricalData", "End");
                historicalData = null;
                Update(commands);
            };
            historicalData.Next();
            Update(commands);
        }
        #region Order
        private object CreateContract()
        {
            return IB.CreateContract(Symbol, SecurityType, Exchange, Currency, Multiplier, PrimaryExchange);
        }
        private object CreateOrder(string action, string orderType, double price, int quantity = 1)
        {
            IBApi.Order order = IB.CreateOrder() as IBApi.Order;
            order.Action = action;
            order.OrderType = orderType; // LMT, MKT, STP, REL, TRAIL
            if (order.OrderType == "LMT") order.LmtPrice = price;
            order.TotalQuantity = quantity;
            order.OutsideRth = true;
            order.ClientId = IB.ClientId;
            if (order.OrderType == "STP") order.AuxPrice = price;
            //order.goodAfterTime = goodAfterTime;
            //order.algoStrategy = "ArrivalPx";
            //ITagValueList values = tws.createTagValueList();
            //values.Add("riskAversion", "Passive");
            //values.Add("maxPctVol", "0.01");
            //order.algoParams = values;
            return order;
        }
        private void ExecuteOrder(int orderId)
        {
            if (MessageBox.Show("Do you really want to place this order?", "Information", 
                MessageBoxButton.YesNo, MessageBoxImage.Information, MessageBoxResult.No) != MessageBoxResult.Yes) 
                return;

            W.X(logger, () =>
            {
                var contract = CreateContract();
                                var price = string.IsNullOrWhiteSpace(Price) ? 0 : double.Parse(Price);
                                var order = CreateOrder(Action, OrderType, price);
                                IB.placeOrder(orderId, contract, order);
                            });
        }
        #endregion
        #region Log
        private void LogObject(Type t, object o)
        {
            var invalidProperties = new string[] { };
            LogHeader(t.Name, "Start");
            foreach (FieldInfo fi in t.GetFields())
                LogInfo(fi.Name, fi.GetValue(o));
            foreach (PropertyInfo pi in t.GetProperties().Where(pi => !invalidProperties.Contains(pi.Name)))
                LogInfo(pi.Name, pi.GetValue(o, null));
            LogHeader(t.Name, "End");
        }
        private void LogHeader(string name, string type)
        {
            LogInfo(" ========== ", "{0} {1}", name, type);
        }
        private void LogInfo(string name, object format = null, params object[] args)
        {
            base.LogInfo(logger, name, format, args);
        }
        #endregion
        public override bool AutoConnect
        {
            get { return Settings.Default.TestClientAutoConnect; }
            set
            {
                Settings.Default.TestClientAutoConnect = value;
                Settings.Default.Save();
            }
        }
    }
}
